#ifndef _VARIANT_STATS_H_
#define _VARIANT_STATS_H_

#include <iostream>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <functional>
#include <utility>
#include <cassert>
#include <vector>
#include <map>

namespace std {
  typedef string genotype;
  typedef string chromosome;
}

enum zygosity {
  HOM = 0,
  HET = 1
};

const int NOT_A_RANGE = -1;

struct GeneLocation {
  std::chromosome chr;
  // This range is inclusive
  int bp_start;
  int bp_end;

  GeneLocation(std::chromosome chr_, int basepair_) {
    chr = chr_;
    bp_start = basepair_;
    bp_end = NOT_A_RANGE;
  }

  GeneLocation(std::chromosome chr_, int bp_start_, int bp_end_) {
    chr = chr_;
    bp_start = bp_start_;
    bp_end = bp_end_;
  }

  friend std::ostream& operator<<(std::ostream& output, const GeneLocation& g);

  // This is a comparison function.  The bp_end is ignored for this purpose.
  // To be more specific, a GeneLocation is compared irrespective of
  // whether it's a range or just one bp.
  bool operator<(const GeneLocation& b) const { 
    if (chr == b.chr) { 
      return bp_start < b.bp_start;
    }
    if ((chr != "X" && chr != "Y") && (b.chr != "X" && b.chr != "Y")) {
      if (atoi(chr.c_str()) < atoi(b.chr.c_str())) {
	return true;
      }
      return false;
    }

    if (chr == "X" && b.chr == "Y") {
      return true;
    }
    if (chr == "Y" && b.chr == "X") {
      return false;
    }
    //    The other one must be numeric
    if (chr == "X" || chr == "Y") {
      return false;
    }
    
    // Same
    if (b.chr == "X" || b.chr == "Y") {
      return true;
    }
    
    std::cerr << *this << "\t" << b << std::endl;
    assert(0);
  }

  bool operator==(const GeneLocation& b) const {
    if (chr == b.chr && bp_start == b.bp_start && bp_end == b.bp_end) {
      return true;
    }
    return false;
  }

  // Returns true if the passed in gene location is contained inside
  // this one.  I.e. chromosome is the same, and, if it's a range, if
  // b "fits" into the range of this one.
  bool isContained(const GeneLocation& b) const {
    if (chr != b.chr) {
      return false;
    }

    // If we're just a chromosome, then b is contained if it's the
    // same chromosome, no matter what it's basepair bounds are.
    if (bp_start == NOT_A_RANGE && bp_end == NOT_A_RANGE) {
      return true;
    }

    // If we're a single nucleotide, then b is contained iff it's
    // the same nucleotide
    if (bp_end == NOT_A_RANGE && b.bp_end == NOT_A_RANGE) {
      return bp_start == b.bp_start;
    }

    // If we're a range, then b is contained if it's the same range
    // or smaller (including 1 nucleotide)
    if (bp_start != NOT_A_RANGE && bp_end != NOT_A_RANGE) {
      if (b.bp_start != NOT_A_RANGE && b.bp_end != NOT_A_RANGE) {
	return bp_start <= b.bp_start && bp_end >= b.bp_end;
      }
      
      if (b.bp_end == NOT_A_RANGE) {
	return bp_start <= b.bp_start && bp_end > b.bp_start;
      }

      // Otherwise, b must be an entire chromosome, and we're a range,
      // so it can't fit.
      return false;
    }
    
    assert(0);
  }
};

std::ostream& operator<<(std::ostream& output, const GeneLocation& g) {
  if (g.bp_start != NOT_A_RANGE && g.bp_end != NOT_A_RANGE) {
    output << g.chr << "/[" << g.bp_start << "-" << g.bp_end << "]";
  } else if (g.bp_start != NOT_A_RANGE) {
    output << g.chr << "/" << g.bp_start;
  } else {
    output << g.chr;
  }
  
  return output;
}

struct VariantAndCounts {
  std::map<std::genotype, int> genotypeCounts;
  std::vector<std::string> alleles;
  // The following two are only filled in for heterozygous calls.
  int ref_reads;
  int alt_reads;
  VariantAndCounts() {
    ref_reads = 0;
    alt_reads = 0;
  }
  std::map<std::pair<zygosity, std::string>, int> alleleReadCount;
};

struct TargetVariant {
  GeneLocation location;
  TargetVariant(std::chromosome chr_, int basepair_) : 
  location(chr_, basepair_) {
  }
  // This records information about each variant.
  // Each element corresponds to information from ONE vcf file
  std::vector<VariantAndCounts*> variant_stats;
};


#endif
